package com.example.themeleaf.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.example.themeleaf.dao.SenseDataMapper;
import com.example.themeleaf.dao.SenseTaskInfoMapper;
import com.example.themeleaf.entity.SenseData;
import com.example.themeleaf.entity.SenseTaskInfo;
import com.example.themeleaf.entity.UserAttributeSet;
import com.example.themeleaf.entity.UserBaicInfoInBlockchain;
import com.example.themeleaf.service.SenseDataService;
import org.hyperledger.fabric.gateway.Contract;
import org.hyperledger.fabric.gateway.ContractException;
import org.hyperledger.fabric.gateway.Network;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ch.hsr.geohash.GeoHash;

import javax.jws.Oneway;
import java.util.*;
import java.util.concurrent.TimeoutException;

/**
 * @author lamaxiya
 * @version 1.0
 * @date 2021/4/12 15:59
 */
@Service
public class SenseDataServiceImpl implements SenseDataService {
    @Autowired
    SenseDataMapper senseDataMapper;
    @Autowired
    SenseTaskInfoMapper senseTaskInfoMapper;
    @Override
    public int insertSenseData(SenseData senseData) {
        String taskid = senseData.getTaskid();
        String userid = senseData.getUserid();
        SenseData senseData1 = senseDataMapper.selectByTaskAndUser(taskid, userid);
        if(senseData1 != null){
//            已经插入数据
            return 1;
        }else{
//            插入数据
            senseDataMapper.insert(senseData);
        }
        return 0;
    }

    @Override
    public boolean checkExistSenseData(String taskid, String userid) {
        SenseData senseData = senseDataMapper.selectByTaskAndUser(taskid, userid);
        if (senseData != null) {
//            已经存在数据
            return true;
        }
        return false;
    }

    @Override
    public List<String> getAllSenseUser(String taskid) {
        List<SenseData> senseData = senseDataMapper.selectByPrimaryKey(taskid);
        List<String> userlist = new ArrayList<>();
        for (SenseData s:senseData
             ) {
            userlist.add(s.getUserid());
        }
        return userlist;
    }
    /**
     * 主要数据可信评估算法，链上信誉更新和奖励更新
     * @param taskid
     * @return
     */
    @Override
    public void dataEvaluation(String taskid) {
        List<SenseData> senseData = senseDataMapper.selectByPrimaryKey(taskid);
        int len = senseData.size();
//        相似度矩阵计算
        float[][] simi = new float[len][len];
        float avg = SenseDataServiceImpl.avgData(senseData);
        for (int i = 0; i < len ; i++) {
            for (int j = 0; j < len; j++) {
                simi[i][j] = avg / (Math.abs(senseData.get(i).getSensedata() - senseData.get(j).getSensedata()) + avg);
            }
        }
//        求k近邻
        String[][] kneighbor = new String[len][2];
        int index = 0;
        for (SenseData s:senseData
             ) {
            GeoHash geoHash = GeoHash.withCharacterPrecision(s.getLatitude(), s.getLongitude(), 8);
            System.out.println(geoHash.toBase32());
            kneighbor[index][0] = String.valueOf(index);
            kneighbor[index][1] = geoHash.toBase32();
            index++;
        }
//        二维数组按照第二列排序
        Arrays.sort(kneighbor, new Comparator<String[]>() {
            @Override
            public int compare(String[] o1, String[] o2) {
                return o1[1].compareTo(o2[1]);
            }
        });
//        求相似度--二维
        String[][] simidemo = new String[len][2];
        for (int i = 0; i <len ; i++) {
            simidemo[i][0] = String.valueOf(i);
            simidemo[i][1] = "0";
        }
        for (String[] s:kneighbor
             ) {
            int[] kindex = SenseDataServiceImpl.findK(s, kneighbor);
            List<String[]> tag = new ArrayList<>();
            for (int ind:kindex
                 ) {
                double lai = Math.abs(senseData.get(Integer.parseInt(s[0])).getLatitude() - senseData.get(ind).getLatitude());
                double lon = Math.abs(senseData.get(Integer.parseInt(s[0])).getLongitude() - senseData.get(ind).getLongitude());
                String s1 = GeoHash.withCharacterPrecision(lai, lon, 8).toBase32();
                tag.add(new String[]{String.valueOf(ind), s1});
            }
            tag.sort(new Comparator<String[]>() {
                @Override
                public int compare(String[] o1, String[] o2) {
                    return o1[1].compareTo(o2[1]);
                }
            });
            System.out.println(tag);
            float sum = 0;
            for (int i = 0; i < 3; i++) {
                String[] strings = tag.get(i);
                sum += simi[Integer.parseInt(s[0])][i];
            }
            simidemo[Integer.parseInt(s[0])][1] = String.valueOf(sum / 3);
        }
//        结果排序降序
        Arrays.sort(simidemo, new Comparator<String[]>() {
            @Override
            public int compare(String[] o1, String[] o2) {
                return o2[1].compareTo(o1[1]);
            }
        });
        for (int i = 0; i < simidemo.length; i++) {
            System.out.println(senseData.get(Integer.parseInt(simidemo[i][0])).getUserid());
        }
//        设置阈值假设--90%
        double threshold = len * 0.9;
//        获取区块链网关
        Network network = FabricGateway.gateway.getNetwork("mychannel");
        Contract userattributesetupload = network.getContract("userattributesetupload");
        try {
//            读取链上任务对应用户属性集，获取期望价格
            byte[] bytes = userattributesetupload.evaluateTransaction("ReadByTask_id", taskid);
            List<UserAttributeSet> attributeSetList = new ArrayList<>();
            attributeSetList = JSONObject.parseArray(new String(bytes), UserAttributeSet.class);
//            读取链上用户信誉值
            Contract contract = network.getContract("userbasicinfo");
            byte[] result = contract.evaluateTransaction("ReadAllUserInfo", "1", "z");
            List<UserBaicInfoInBlockchain> list;
            list = JSONObject.parseArray(new String(result), UserBaicInfoInBlockchain.class);
//            调用链上合约执行信誉和奖励值更新算法
            Contract userbasicinfo = network.getContract("userbasicinfo");
            for (int i = 0; i < simidemo.length; i++) {
                String isOutlier = "false";
                String userid = senseData.get(Integer.parseInt(simidemo[i][0])).getUserid();
                String datasimi = String.valueOf(simidemo[i][1]);
                if((i+1)>(int) threshold){
                    isOutlier = "true";
                }
                String bonus = SenseDataServiceImpl.getExpectedPrice(attributeSetList, userid);
                byte[] bytes1 = userbasicinfo.submitTransaction("UdpateUserCreditAndReward", userid, datasimi, isOutlier, bonus);
                System.out.println(new String(bytes1));
            }
            SenseTaskInfo senseTaskInfo = senseTaskInfoMapper.selectByPrimaryKey(taskid);
            senseTaskInfo.setTask_status("已结束");
            senseTaskInfoMapper.updateTaskStatus(senseTaskInfo);
        } catch (ContractException | InterruptedException | TimeoutException e) {
            e.printStackTrace();
        }
    }

    private static String getExpectedPrice(List<UserAttributeSet> attributeSetList, String userid) {
        for (UserAttributeSet user: attributeSetList
             ) {
            if(user.getRecord().getUser_id().equals(userid)){
                return String.valueOf(user.getRecord().getEp());
            }
        }
        return "0";
    }

    //  查找附近k个潜在邻居
    private static int[] findK(String[] s, String[][] kneighbor) {
        List<Integer> res = new ArrayList<>();
        int len1 = kneighbor.length;
        int index = 0;
        for (int i = 0; i < len1; i++) {
            if(kneighbor[i][0] == s[0]) {
                index = i;
                break;
            }
        }
        int left = index;
        int right = index;
        int step = 1;
        while(right<len1){
            if(right + step <len1 && step<=3) {
                res.add(right+step);
                step++;
            }else{
                break;
            }
        }
        int step1 = 1;
        while(left>=0) {
            if(left-step1 >=0 && step1<=3){
                res.add(left-step1);
                step1++;
            }else {
                break;
            }
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }

    //    请数据平均值
    public static float avgData(List<SenseData> senseData){
        float sum = 0;
        for (SenseData s:senseData
             ) {
            sum += s.getSensedata();
        }
        return sum / senseData.size();
    }



}
